package com.ruleengine.compiler.impl;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.apache.log4j.Logger;

import com.ruleengine.compiler.ITaskCompiler;
import com.ruleengine.compiler.exception.CompilerException;
import com.ruleengine.instruction.InstructionManager;
import com.ruleengine.instruction.InstructionSet;
import com.ruleengine.instruction.InstructionType;
import com.ruleengine.instruction.impl.GotoInstruction;
import com.ruleengine.instruction.impl.JumpToFinallyInstruction;
import com.ruleengine.instruction.impl.ResetFromFinallyInstruction;
import com.ruleengine.instruction.impl.ThrowExceptionInstruction;
import com.ruleengine.task.ITask;
import com.ruleengine.task.config.TaskConfigManager;
import com.ruleengine.task.handler.IExceptionHandler;
import com.ruleengine.task.handler.IHandler;

public abstract class BaseCompiler<E extends ITask> implements ITaskCompiler<E> {
    private static Logger logger = Logger.getLogger(BaseCompiler.class);

    public void comipleTask(E task, InstructionSet instSet)
            throws CompilerException {
        // TODO Auto-generated method stub
        // 异常处理器的try段开始指令偏移量（绝对）
        int excpFrom = instSet.getInstructions().size();
        
        /* * 编译子指令 */
        comipleSubTask(task, instSet);
        
        /* 编译异常处理器 */
        IExceptionHandler ehandler = task.getExceptionHandler();
        if (ehandler == null) {
            // 没有异常处理器
            logger.debug(task.getTaskType()
                    + " don't configurate any no exception handler!");
        } else {
            // 配置了异常处理器
            comipleExceptionHandler(ehandler, instSet, excpFrom);
        }
        
        if (this instanceof WorkflowTaskCompiler) {
            //记录异常处理表,将所有指令都执行异常保护
            instSet.addExceptionHandler(0, instSet.getInstructions().size(), instSet.getInstructions().size(),
                    Arrays.asList("java.lang.Exception"));

            // 插入终止指令
            instSet.addInstruction(InstructionManager.getInstance()
                    .createInstruction(InstructionType.TERMINATE));
            
            
        }
    }
    
    private void comipleExceptionHandler(IExceptionHandler ehandler,
            InstructionSet instSet, int excpFrom)
            throws CompilerException {
        // 异常处理catch段开始指令偏移量（绝对）
        int excpTo = instSet.getInstructions().size();

        // 暂存goto指令，便于后续动态修改偏移量
        List<GotoInstruction> gotos = new ArrayList<GotoInstruction>();
        List<JumpToFinallyInstruction> jumps = new ArrayList<JumpToFinallyInstruction>();

        // try块的结尾
        if (ehandler.getFinally() != null) {
            // 如果配置了finally块
            // 配置了finally块后，需要在try块后插入如下指令：
            // jump指令
            JumpToFinallyInstruction jtIn = InstructionManager.getInstance()
                    .createInstruction(InstructionType.JUMPTOFINALLY);
            jumps.add(jtIn);
            instSet.addInstruction(jtIn);
            
            // throw指令
            ThrowExceptionInstruction thIn = InstructionManager.getInstance()
                    .createInstruction(InstructionType.THROWEXCEPTION);
            instSet.addInstruction(thIn);
        }

        // 在Exception配置之前附加goto指令，offset暂时以0代替
        GotoInstruction excpGotoIn = InstructionManager.getInstance()
                .createInstruction(InstructionType.GOTO);
        instSet.addInstruction(excpGotoIn);
        gotos.add(excpGotoIn);

        // 处理每一个异常指令
        List<IHandler> inters = ehandler.getHandlers();
        for (int i = 0; i < inters.size(); i++) {
            // 子指令开始偏移量
            int begin = instSet.getInstructions().size();

            // 编译异常处理块子指令
            IHandler handler = inters.get(i);
            List<ITask> ta = handler.getHandlerTask();
            if(ta != null && ta.size() > 0){
                //catch块配置了处理流程
                for(ITask t : ta){
                    //编译每一个子指令
                    ITaskCompiler c = TaskConfigManager.getInstance().getTaskCompiler(
                        t.getTaskType());
                    c.comipleTask(t, instSet);
                }
            }

            if (ehandler.getFinally() != null) {
                // 如果配置了finally块
                // 配置了finally块后，需要在每个catch块后插入如下指令：
                // jump指令
                JumpToFinallyInstruction jtIn = InstructionManager
                        .getInstance().createInstruction(
                                InstructionType.JUMPTOFINALLY);
                jumps.add(jtIn);
                instSet.addInstruction(jtIn);

                // throw指令
                ThrowExceptionInstruction thIn = InstructionManager
                        .getInstance().createInstruction(
                                InstructionType.THROWEXCEPTION);
                instSet.addInstruction(thIn);
            }

            // 子指令结束偏移量
            int end = instSet.getInstructions().size();

            // 更新暂存goto指令的偏移量
            for (GotoInstruction go : gotos) {
                int offset = end - begin;
                go.setGotoOffset(go.getGotoOffset() + offset + 1);
            }

            // 处理附加的goto指令
            // 每个handler之后，需要插入附加的goto指令，offset暂时以0代替
            GotoInstruction gotoIn = InstructionManager.getInstance()
                    .createInstruction(InstructionType.GOTO);
            instSet.addInstruction(gotoIn);
            gotos.add(gotoIn);

            // 记录异常处理表,注意excpTo需要附加一个额外goto指令，即excpTo+1
            instSet.addExceptionHandler(excpFrom, excpTo, begin,
                    handler.getCauses());

            if (ehandler.getFinally() != null && i < inters.size() - 1) {
                /* 如果配置了finally块,不是最后一个catch块，因为最后一个catch块是默认的，
                 * 目的就是要抛出没有捕捉到的异常，所以这个catch不需要受异常表保护
                 */
                // 配置了finally块后，需要在给catch块配置异常表
                instSet.addExceptionHandler(begin, end - 2, end - 2, Arrays
                        .asList("java.lang.Throwable"));

            }

        }

        // 编译finally块
        ITask t = ehandler.getFinally();
        if (t != null) {
            // 配置了finally

            // 子指令开始偏移量
            int begin = instSet.getInstructions().size();

            // 附加的reset指令，重置finally
            ResetFromFinallyInstruction resetIn = InstructionManager
            .getInstance().createInstruction(
                    InstructionType.RESETFROMFINALLY);

            //修改jump指令的偏移量
            for(JumpToFinallyInstruction jtIn : jumps){
                int jumpIndex = instSet.indexOf(jtIn);
                //修改jump指令的偏移量
                jtIn.setGotoOffset(begin-jumpIndex-1);
                //指定reset指令的对应jump指令
                resetIn.addJumpInstructionIndex(jumpIndex);
            }
            
            // 编译finally的子指令
            ITaskCompiler c = TaskConfigManager.getInstance().getTaskCompiler(
                    t.getTaskType());
            c.comipleTask(t, instSet);
            
            // 插入附加的reset指令，
            instSet.addInstruction(resetIn);

            // 子指令结束偏移量
            int end = instSet.getInstructions().size();
            
            // 更新暂存goto指令的偏移量
            for (GotoInstruction go : gotos) {
                int offset = end - begin;
                go.setGotoOffset(go.getGotoOffset() + offset);
            }
        }
    }
    public abstract void comipleSubTask(E task, InstructionSet instSet)
            throws CompilerException;
}
